package de.upb.aquarium.water.pump;

import java.util.LinkedList;
import java.util.List;

import javax.media.opengl.GL;

import de.upb.aquarium.api.objects.IPaintable;
import de.upb.aquarium.api.utility.VectorDouble;
import de.upb.aquarium.api.utility.VectorFloat;
import de.upb.aquarium.water.WaveGenerator;
import de.upb.aquarium.water.WaveGenerator.WaveStrength;

public class WaterParticleManager implements IPaintable
{
	private static WaterParticleManager instance = null;
	
	public static WaterParticleManager getInstance()
	{
		if (instance == null)
		{
			instance = new WaterParticleManager();
		}
		
		return instance;
	}
	
	private final VectorDouble surfacePlane;
	private VectorFloat surfacePosition;

	private final List<WaterParticle> particles;
	private final List<WaterPump> pumps;
	private WaveGenerator waveGenerator;

	private static final int numParticles = 250; 
	
	public WaterParticleManager() {
		this.surfacePlane = new VectorDouble(4);
		this.particles = new LinkedList<WaterParticle>();
		this.pumps = new LinkedList<WaterPump>();
	}
	
	public void paint(GL gl)
	{
		List<WaterParticle> toRemove = new LinkedList<WaterParticle>();
		
		for (WaterParticle particle : particles)
		{
			
			particle.paint(gl);
			
			if (!particle.isAlive())
			{
				toRemove.add(particle);
				
				if (particle.isBumper())
				{
					generateWave(particle.getPosition());
				}
			}
		}
		
		particles.removeAll(toRemove);
		
		if (particles.size() < numParticles)
		{
			particles.addAll(pumps.get(0).generate(numParticles - particles.size()));
		}
	}
	
	private void generateWave(VectorFloat position)
	{	
		VectorFloat relPos = position.sub(surfacePosition);
		waveGenerator.generateWave(relPos.data[0], relPos.data[2], WaveStrength.SMALL);
	}

	private void addWaveStartCoordinates(List<VectorDouble> coordinates)
	{
		for (VectorDouble c : coordinates)
		{
			VectorDouble relPos = c.sub(new VectorDouble(surfacePosition.data));
			
			waveGenerator.addPotentialWave(relPos.data[0], relPos.data[2], WaveStrength.SMALL);
		}
	}

	public void setSurfacePlane(double x, double y, double z, double dist)
	{
		surfacePlane.data[0] = x;
		surfacePlane.data[1] = y;
		surfacePlane.data[2] = z;
		surfacePlane.data[3] = dist;
	}
	
	public VectorDouble getSurfacePlane()
	{
		return surfacePlane;
	}
	
	public void addPump(WaterPump pump)
	{
		pumps.add(pump);
		addWaveStartCoordinates(pump.getPotentialWavePositions());
	}
	
	public void removePump(WaterPump pump)
	{
		pumps.remove(pump);
	}
	
	public int getNumParticles()
	{
		return particles.size();
	}

	public WaveGenerator getWaveGenerator() {
		return waveGenerator;
	}

	public void setWaveGenerator(WaveGenerator waveGenerator) {
		this.waveGenerator = waveGenerator;
	}
	
	public VectorFloat getSurfacePosition() {
		return surfacePosition;
	}

	public void setSurfacePosition(VectorFloat surfacePosition) {
		this.surfacePosition = surfacePosition;
	}
}
